#include "keyboard.h"
#include "interrupt.h"
#include "print.h"
#include "io.h"
#include "ioqueue.h"

/* 0x60既作为输入也作为输出 */
#define KBD_BUF_PORT        0x60

#define esc                 '\033'                 
#define backspace           '\b'
#define tab                 '\t'
#define enter               '\n'


#define ctrl_l_char         0
#define ctrl_r_char         0
#define shift_l_char        0
#define shift_r_char        0
#define alt_l_char          0
#define alt_r_char          0
#define caps_lock_char      0

#define ctrl_l_make_code    0x1d
#define ctrl_r_make_code    0xe01d  
#define shift_l_make_code   0x2a
#define shift_r_make_code   0x36 
#define alt_l_make_code     0X38
#define alt_r_make_code     0xe038 
#define caps_lock_make_code 0x3a

/* 以通码make_code为索引的二维数组 */
static char keymap[][2] = {
/* 扫描码   未与shift组合  与shift组合*/
/* ---------------------------------- */
/* 0x00 */	{0,	0},		
/* 0x01 */	{esc,	esc},		
/* 0x02 */	{'1',	'!'},		
/* 0x03 */	{'2',	'@'},		
/* 0x04 */	{'3',	'#'},		
/* 0x05 */	{'4',	'$'},		
/* 0x06 */	{'5',	'%'},		
/* 0x07 */	{'6',	'^'},		
/* 0x08 */	{'7',	'&'},		
/* 0x09 */	{'8',	'*'},		
/* 0x0A */	{'9',	'('},		
/* 0x0B */	{'0',	')'},		
/* 0x0C */	{'-',	'_'},		
/* 0x0D */	{'=',	'+'},		
/* 0x0E */	{backspace, backspace},	
/* 0x0F */	{tab,	tab},		
/* 0x10 */	{'q',	'Q'},		
/* 0x11 */	{'w',	'W'},		
/* 0x12 */	{'e',	'E'},		
/* 0x13 */	{'r',	'R'},		
/* 0x14 */	{'t',	'T'},		
/* 0x15 */	{'y',	'Y'},		
/* 0x16 */	{'u',	'U'},		
/* 0x17 */	{'i',	'I'},		
/* 0x18 */	{'o',	'O'},		
/* 0x19 */	{'p',	'P'},		
/* 0x1A */	{'[',	'{'},		
/* 0x1B */	{']',	'}'},		
/* 0x1C */	{enter,  enter},
/* 0x1D */	{ctrl_l_char, ctrl_l_char},
/* 0x1E */	{'a',	'A'},		
/* 0x1F */	{'s',	'S'},		
/* 0x20 */	{'d',	'D'},		
/* 0x21 */	{'f',	'F'},		
/* 0x22 */	{'g',	'G'},		
/* 0x23 */	{'h',	'H'},		
/* 0x24 */	{'j',	'J'},		
/* 0x25 */	{'k',	'K'},		
/* 0x26 */	{'l',	'L'},		
/* 0x27 */	{';',	':'},		
/* 0x28 */	{'\'',	'"'},		
/* 0x29 */	{'`',	'~'},		
/* 0x2A */	{shift_l_char, shift_l_char},	
/* 0x2B */	{'\\',	'|'},		
/* 0x2C */	{'z',	'Z'},		
/* 0x2D */	{'x',	'X'},		
/* 0x2E */	{'c',	'C'},		
/* 0x2F */	{'v',	'V'},		
/* 0x30 */	{'b',	'B'},		
/* 0x31 */	{'n',	'N'},		
/* 0x32 */	{'m',	'M'},		
/* 0x33 */	{',',	'<'},		
/* 0x34 */	{'.',	'>'},		
/* 0x35 */	{'/',	'?'},
/* 0x36	*/	{shift_r_char, shift_r_char},	
/* 0x37 */	{'*',	'*'},    	
/* 0x38 */	{alt_l_char, alt_l_char},
/* 0x39 */	{' ',	' '},		
/* 0x3A */	{caps_lock_char, caps_lock_char}
/*其它按键暂不处理*/
};

static uint8_t shift_status = 0, ctrl_status = 0, caps_lock_status = 0, alt_status = 0;

struct ioqueue keyboard_ioqueue;

/* 根据第一套键盘扫描码制定的 */
static void _keyboard_irq_handler(uint8_t vec_num){
    static uint8_t _is_extra = 0;
    uint8_t _temp = io_in_byte(KBD_BUF_PORT);
    uint8_t _is_break_code = 0;
    uint16_t _scan_code = 0;
    /* 如果为0xe0开头的 则为扩展键盘扫描码 需要获取下一个字节 */
    if(_temp == 0xe0){
        _is_extra = 1;
        return;
    }

    if(_is_extra == 1){
        /* 如果是扩展键盘扫描码 需要合成 */
        _is_extra = 0;
        _scan_code = 0xe000 | _temp;
    }else{
        /* 如果不是扩展键盘扫描码 原值即可 */
        _scan_code = _temp;
    }
    /* 在第一套键盘扫描码标准中断码只需要将通码的第7位置1即可 */
    _is_break_code = _scan_code & 0x0080;

    if(_is_break_code){
        /* 恢复成通码的格式 */
        _scan_code &= 0xff7f;
        /* 如果是断码的话 只需要判断控制键是否松开就行了 */
        if(_scan_code == ctrl_l_make_code || _scan_code == ctrl_r_make_code){
            ctrl_status = 0;
        }else if(_scan_code == alt_l_make_code || _scan_code == alt_r_make_code){
            alt_status = 0;
        }else if(_scan_code == shift_l_make_code || _scan_code == shift_r_make_code){
            shift_status = 0;
        }
        /* 这里不判断cap大写锁定是因为cap相当于自锁开关 我们只需要每次按下改变状态就可以 */
    }else{
        /* 这里是通码部分的处理 我们只支持主要键位 所以把我们支持的键位区间写一下 */
        if((_scan_code > 0 && _scan_code < 0x3b) || 
            _scan_code == ctrl_r_make_code || _scan_code == alt_r_make_code){
            uint8_t ___is_second_func = 0;
            /* 这里对于字母和其他分开判断 因为字母受大写锁定影响 其他不受 */
            if ((_scan_code < 0x0e) || (_scan_code == 0x29) || \
                (_scan_code == 0x1a) || (_scan_code == 0x1b) || \
                (_scan_code == 0x2b) || (_scan_code == 0x27) || \
                (_scan_code == 0x28) || (_scan_code == 0x33) || \
                (_scan_code == 0x34) || (_scan_code == 0x35)) {
                    
                    /****** 代表两个字母的键 ********
                     0x0e 数字'0'~'9',字符'-',字符'='
                        0x29 字符'`'
                        0x1a 字符'['
                        0x1b 字符']'
                        0x2b 字符'\\'
                        0x27 字符';'
                        0x28 字符'\''
                        0x33 字符','
                        0x34 字符'.'
                        0x35 字符'/' 
                    *******************************/

                   if(shift_status == 1){
                        ___is_second_func = 1;
                   }
            }else{
                /* 在这里则认定为是字母键 */
                if(caps_lock_status == 1 && shift_status == 1){
                    /* 抵消 */
                    ___is_second_func = 0;
                }else if(caps_lock_status == 1 || shift_status == 1){
                    ___is_second_func = 1;
                }else{
                    ___is_second_func = 0;
                }
            }

            uint8_t __index =   _scan_code &= 0x007f;
            uint8_t __ch = keymap[__index][___is_second_func];
            if(__ch != 0){
                if(!ioqueue_full(&keyboard_ioqueue)){
                    put_char(__ch);
                    ioqueue_put_char(&keyboard_ioqueue, __ch);
                }
                return;
            }

            if(_scan_code == ctrl_l_make_code || _scan_code == ctrl_r_make_code){
                ctrl_status = 1;
            }else if(_scan_code == alt_l_make_code || _scan_code == alt_r_make_code){
                alt_status = 1;
            }else if(_scan_code == shift_l_make_code || _scan_code == shift_r_make_code){
                shift_status = 1;
            }else if(_scan_code == caps_lock_make_code){
                if(caps_lock_status == 0){
                    caps_lock_status = 1;
                }else{
                    caps_lock_status = 0;
                }
            }

        }else{
            put_str("unknown key\n");
        }
    }
}

void keyboard_init(void){
    put_str("   keyboard_init init\n");
    ioqueue_init(&keyboard_ioqueue);
    intr_register_callback(0x21, _keyboard_irq_handler);
    put_str("   keyboard_init done\n\n");
}